﻿#include "TextureAnalyzer.h"
#include <numeric>

TextureAnalyzer::TextureAnalyzer(int medianBlurSize,
    int verticalKernelSize,
    int horizontalKernelSize)
    : _medianBlurSize(medianBlurSize),
    _verticalKernelSize(verticalKernelSize),
    _horizontalKernelSize(horizontalKernelSize),
    _meanAngle(0.0f) {
}

cv::Mat TextureAnalyzer::processImage(const cv::Mat& inputImage) {
    cv::Mat grayImage;
    if (inputImage.channels() == 3) {
        cv::cvtColor(inputImage, grayImage, cv::COLOR_BGR2GRAY);
    }
    else {
        grayImage = inputImage.clone();
    }

    cv::Mat shadowRemoved = removeShadow(grayImage);
    cv::Mat trendRemoved = removeIlluminationTrend(shadowRemoved);
    cv::Mat textureEnhanced = enhanceHorizontalTexture(trendRemoved);
    cv::Mat skeleton = skeletonize(textureEnhanced);

    calculateAngles(skeleton);
    return skeleton;
}

cv::Mat TextureAnalyzer::removeShadow(const cv::Mat& img) {
    cv::Mat labImage;
    cv::cvtColor(img, labImage, cv::COLOR_GRAY2BGR); // 先转BGR
    cv::cvtColor(labImage, labImage, cv::COLOR_BGR2Lab);

    std::vector<cv::Mat> labChannels;
    cv::split(labImage, labChannels);
    cv::Mat lChannel = labChannels[0];

    cv::Mat shadowMask;
    cv::threshold(lChannel, shadowMask, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);

    cv::Mat nonShadow = cv::Mat::zeros(img.size(), CV_8U);
    cv::Mat shadowArea = cv::Mat::zeros(img.size(), CV_8U);

    img.copyTo(nonShadow, ~shadowMask);
    img.copyTo(shadowArea, shadowMask);

    cv::Mat compensatedShadow;
    cv::add(shadowArea, 30, compensatedShadow, shadowMask);

    return cv::max(nonShadow, compensatedShadow);
}

cv::Mat TextureAnalyzer::removeIlluminationTrend(const cv::Mat& img) {
    //CLAHE增强
    cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(3.0, cv::Size(8, 8));
    cv::Mat enhanced;
    clahe->apply(img, enhanced);

    //Top-Hat变换去除亮度趋势
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(25, 25));
    cv::Mat background;
    cv::morphologyEx(enhanced, background, cv::MORPH_OPEN, kernel);

    //中值滤波降噪
    cv::Mat trendRemoved0,trendRemoved;
    cv::subtract(enhanced, background, trendRemoved0);
    cv::medianBlur(trendRemoved0, trendRemoved, _medianBlurSize);

    return trendRemoved;
}

cv::Mat TextureAnalyzer::enhanceHorizontalTexture(const cv::Mat& img) {
    //自适应阈值二值化
    cv::Mat bw;
    cv::threshold(img, bw, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);

    //压制垂直纹理
    cv::Mat verticalKernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(1, _verticalKernelSize));
    cv::morphologyEx(bw, bw, cv::MORPH_OPEN, verticalKernel);

     //增强水平纹理
    cv::Mat horizontalKernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(_horizontalKernelSize, 1));
    cv::morphologyEx(bw, bw, cv::MORPH_CLOSE, horizontalKernel);

    //连通域分析去除小噪声
    cv::Mat labels, stats, centroids;
    int numComponents = cv::connectedComponentsWithStats(bw, labels, stats, centroids, 8, CV_32S);
    for (int i = 1; i < numComponents; ++i) {
        if (stats.at<int>(i, cv::CC_STAT_AREA) < 100) {
            bw.setTo(0, labels == i);
        }
    }

    return bw;
}

cv::Mat TextureAnalyzer::skeletonize(const cv::Mat& binaryImg) {
    cv::Mat skeleton = cv::Mat::zeros(binaryImg.size(), CV_8U);
    cv::Mat temp, eroded;
    binaryImg.copyTo(temp);

    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3));

    while (true) {
        cv::erode(temp, eroded, kernel);
        cv::dilate(eroded, temp, kernel);
        cv::subtract(temp, eroded, temp);
        cv::bitwise_or(skeleton, temp, skeleton);

        if (cv::countNonZero(eroded) == 0)
            break;
    }

    return skeleton;
}

void TextureAnalyzer::calculateAngles(const cv::Mat& skeleton) {
    std::vector<cv::Vec4i> lines;
    cv::HoughLinesP(skeleton, lines, 1, CV_PI/180, 60, 100, 20);

    _angles.clear();
    for (const auto& line : lines) {
        int x1 = line[0], y1 = line[1], x2 = line[2], y2 = line[3];
        float angle = std::atan2(y2 - y1, x2 - x1) * 180 / CV_PI;
        _angles.push_back(angle);
    }

    if (!_angles.empty()) {
        _meanAngle = std::accumulate(_angles.begin(), _angles.end(), 0.0f) / _angles.size();
    } else {
        _meanAngle = 0.0f;
    }
}